/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

var echarts = require("../../echarts");

var zrUtil = require("static/plugins/js/zrender/lib/core/util");

var graphic = require("../../util/graphic");

var MapDraw = require("../../component/helper/MapDraw");

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
var HIGH_DOWN_PROP = "__seriesMapHighDown";
var RECORD_VERSION_PROP = "__seriesMapCallKey";

var _default = echarts.extendChartView({
    type: "map",
    render: function (mapModel, ecModel, api, payload) {
        // Not render if it is an toggleSelect action from self
        if (
            payload &&
            payload.type === "mapToggleSelect" &&
            payload.from === this.uid
        ) {
            return;
        }

        var group = this.group;
        group.removeAll();

        if (mapModel.getHostGeoModel()) {
            return;
        } // Not update map if it is an roam action from self

        if (
            !(
                payload &&
                payload.type === "geoRoam" &&
                payload.componentType === "series" &&
                payload.seriesId === mapModel.id
            )
        ) {
            if (mapModel.needsDrawMap) {
                var mapDraw = this._mapDraw || new MapDraw(api, true);
                group.add(mapDraw.group);
                mapDraw.draw(mapModel, ecModel, api, this, payload);
                this._mapDraw = mapDraw;
            } else {
                // Remove drawed map
                this._mapDraw && this._mapDraw.remove();
                this._mapDraw = null;
            }
        } else {
            var mapDraw = this._mapDraw;
            mapDraw && group.add(mapDraw.group);
        }

        mapModel.get("showLegendSymbol") &&
            ecModel.getComponent("legend") &&
            this._renderSymbols(mapModel, ecModel, api);
    },
    remove: function () {
        this._mapDraw && this._mapDraw.remove();
        this._mapDraw = null;
        this.group.removeAll();
    },
    dispose: function () {
        this._mapDraw && this._mapDraw.remove();
        this._mapDraw = null;
    },
    _renderSymbols: function (mapModel, ecModel, api) {
        var originalData = mapModel.originalData;
        var group = this.group;
        originalData.each(
            originalData.mapDimension("value"),
            function (value, originalDataIndex) {
                if (isNaN(value)) {
                    return;
                }

                var layout = originalData.getItemLayout(originalDataIndex);

                if (!layout || !layout.point) {
                    // Not exists in map
                    return;
                }

                var point = layout.point;
                var offset = layout.offset;
                var circle = new graphic.Circle({
                    style: {
                        // Because the special of map draw.
                        // Which needs statistic of multiple series and draw on one map.
                        // And each series also need a symbol with legend color
                        //
                        // Layout and visual are put one the different data
                        fill: mapModel.getData().getVisual("color"),
                    },
                    shape: {
                        cx: point[0] + offset * 9,
                        cy: point[1],
                        r: 3,
                    },
                    silent: true,
                    // Do not overlap the first series, on which labels are displayed.
                    z2: 8 + (!offset ? graphic.Z2_EMPHASIS_LIFT + 1 : 0),
                }); // Only the series that has the first value on the same region is in charge of rendering the label.
                // But consider the case:
                // series: [
                //     {id: 'X', type: 'map', map: 'm', {data: [{name: 'A', value: 11}, {name: 'B', {value: 22}]},
                //     {id: 'Y', type: 'map', map: 'm', {data: [{name: 'A', value: 21}, {name: 'C', {value: 33}]}
                // ]
                // The offset `0` of item `A` is at series `X`, but of item `C` is at series `Y`.
                // For backward compatibility, we follow the rule that render label `A` by the
                // settings on series `X` but render label `C` by the settings on series `Y`.

                if (!offset) {
                    var fullData = mapModel.mainSeries.getData();
                    var name = originalData.getName(originalDataIndex);
                    var fullIndex = fullData.indexOfName(name);
                    var itemModel =
                        originalData.getItemModel(originalDataIndex);
                    var labelModel = itemModel.getModel("label");
                    var hoverLabelModel = itemModel.getModel("emphasis.label");
                    var regionGroup = fullData.getItemGraphicEl(fullIndex); // `getFormattedLabel` needs to use `getData` inside. Here
                    // `mapModel.getData()` is shallow cloned from `mainSeries.getData()`.
                    // FIXME
                    // If this is not the `mainSeries`, the item model (like label formatter)
                    // set on original data item will never get. But it has been working
                    // like that from the begining, and this scenario is rarely encountered.
                    // So it won't be fixed until have to.

                    var normalText = zrUtil.retrieve2(
                        mapModel.getFormattedLabel(fullIndex, "normal"),
                        name
                    );
                    var emphasisText = zrUtil.retrieve2(
                        mapModel.getFormattedLabel(fullIndex, "emphasis"),
                        normalText
                    );
                    var highDownRecord = regionGroup[HIGH_DOWN_PROP];
                    var recordVersion = Math.random(); // Prevent from register listeners duplicatedly when roaming.

                    if (!highDownRecord) {
                        highDownRecord = regionGroup[HIGH_DOWN_PROP] = {};
                        var onEmphasis = zrUtil.curry(onRegionHighDown, true);
                        var onNormal = zrUtil.curry(onRegionHighDown, false);
                        regionGroup
                            .on("mouseover", onEmphasis)
                            .on("mouseout", onNormal)
                            .on("emphasis", onEmphasis)
                            .on("normal", onNormal);
                    } // Prevent removed regions effect current grapics.

                    regionGroup[RECORD_VERSION_PROP] = recordVersion;
                    zrUtil.extend(highDownRecord, {
                        recordVersion: recordVersion,
                        circle: circle,
                        labelModel: labelModel,
                        hoverLabelModel: hoverLabelModel,
                        emphasisText: emphasisText,
                        normalText: normalText,
                    }); // FIXME
                    // Consider set option when emphasis.

                    enterRegionHighDown(highDownRecord, false);
                }

                group.add(circle);
            }
        );
    },
});

function onRegionHighDown(toHighOrDown) {
    var highDownRecord = this[HIGH_DOWN_PROP];

    if (
        highDownRecord &&
        highDownRecord.recordVersion === this[RECORD_VERSION_PROP]
    ) {
        enterRegionHighDown(highDownRecord, toHighOrDown);
    }
}

function enterRegionHighDown(highDownRecord, toHighOrDown) {
    var circle = highDownRecord.circle;
    var labelModel = highDownRecord.labelModel;
    var hoverLabelModel = highDownRecord.hoverLabelModel;
    var emphasisText = highDownRecord.emphasisText;
    var normalText = highDownRecord.normalText;

    if (toHighOrDown) {
        circle.style.extendFrom(
            graphic.setTextStyle(
                {},
                hoverLabelModel,
                {
                    text: hoverLabelModel.get("show") ? emphasisText : null,
                },
                {
                    isRectText: true,
                    useInsideStyle: false,
                },
                true
            )
        ); // Make label upper than others if overlaps.

        circle.__mapOriginalZ2 = circle.z2;
        circle.z2 += graphic.Z2_EMPHASIS_LIFT;
    } else {
        graphic.setTextStyle(
            circle.style,
            labelModel,
            {
                text: labelModel.get("show") ? normalText : null,
                textPosition: labelModel.getShallow("position") || "bottom",
            },
            {
                isRectText: true,
                useInsideStyle: false,
            }
        ); // Trigger normalize style like padding.

        circle.dirty(false);

        if (circle.__mapOriginalZ2 != null) {
            circle.z2 = circle.__mapOriginalZ2;
            circle.__mapOriginalZ2 = null;
        }
    }
}

module.exports = _default;
